//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <linknode.h>
#include "dbgstub.h"

class DbgStubSink : public DLinkNode
{
public:
    DbgStubSink() :
        m_pIStub(NULL),
        m_pDebugger(NULL)
    {
    }

    void Initialize(
        const wchar_t * wszServiceName, IStub *pIStub, DebuggerStub* pDebugger);


public:
    wchar_t         m_wszServiceName[c_uMaxLengthOfDriverServiceName + 1];
    IStub         * m_pIStub;
    DebuggerStub  * m_pDebugger;
};

void DbgStubSink::Initialize(
    const wchar_t *wszServiceName,
    IStub *pIStub,
    DebuggerStub* pDebugger)
{
    assert(pIStub != NULL);
    assert(pDebugger != NULL);

    wcsncpy(m_wszServiceName, wszServiceName, c_uMaxLengthOfDriverServiceName);
    m_wszServiceName[c_uMaxLengthOfDriverServiceName] = L'\0';
    m_pIStub    = pIStub;
    m_pDebugger = pDebugger;
}

class DbgStubManager : public CDeviceDriver
{
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

    DbgStubManager()
#ifdef DEBUG_KMUTEX
	: m_listLock(__FILE__, __LINE__)
#endif //DEBUG_KMUTEX
    {
        m_list.Initialize();
    }

private:
    DLinkNode   m_list;
    KMutex      m_listLock;
};

ECode DbgStubManager::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode DbgStubManager::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode DbgStubManager::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;
    DbgStubSink * pDbgStubSink = NULL;
    DebuggerStub * pDebugger = NULL;
    IStub * pIStub;
    wchar_t * pServiceName;

    switch(nControlCode) {
        case DBGMANAGER_CODE_CREATE_DBGSTUB:
        {
            if (inBuffer.IsNullOrEmpty()
                || (uint_t)inBuffer.GetCapacity()
                    > sizeof(wchar_t) * c_uMaxLengthOfDriverServiceName) {
                return E_INVALID_ARGUMENT;
            }

            pServiceName = (wchar_t *)inBuffer.GetPayload();

            // create a debugger stub
            //
            pDebugger = new DebuggerStub;
            if (NULL == pDebugger) {
                return E_OUT_OF_MEMORY;
            }
            pDebugger->AddRef();

            pDbgStubSink = new DbgStubSink;
            if (NULL == pDbgStubSink) {
                pDebugger->Release();
                return E_OUT_OF_MEMORY;
            }

            ec = DzRegisterService(pServiceName, (IDeviceDriver *)pDebugger, &pIStub);
            if (SUCCEEDED(ec)) {
                pDbgStubSink->Initialize(pServiceName, pIStub, pDebugger);
                m_listLock.Lock();
                m_list.InsertLast(pDbgStubSink);
                m_listLock.Unlock();
            }
            else {
                delete pDbgStubSink;
                pDebugger->Release();
            }
        }
        break;

        case DBGMANAGER_CODE_DESTROY_DBGSTUB:
        {
            if (inBuffer.IsNullOrEmpty()
                || (uint_t)inBuffer.GetCapacity()
                    > sizeof(wchar_t) * c_uMaxLengthOfDriverServiceName) {
                return E_INVALID_ARGUMENT;
            }

            pServiceName = (wchar_t *)inBuffer.GetPayload();

            m_listLock.Lock();

            DbgStubSink * pNode;
            ForEachDLinkNode(DbgStubSink *, pNode, &m_list) {
                if (0 == wcscmp(pServiceName, pNode->m_wszServiceName)) {
                    pDbgStubSink = pNode;
                    break;
                }
            }

            m_listLock.Unlock();

            if (NULL == pDbgStubSink) {
                return E_INVALID_ARGUMENT;
            }

            pDbgStubSink->Detach();

            // make sure to detach target process
            //
            pDbgStubSink->m_pDebugger->Control(
                DBGSTUB_CTRL_CODE_DETACH_PROCESS, NULL_MEMORYBUF, NULL, NULL);

            DzUnregisterService(
                pDbgStubSink->m_wszServiceName, pDbgStubSink->m_pIStub);

            pDbgStubSink->m_pDebugger->Release();
            delete pDbgStubSink;
        }
        break;

        case DBGMANAGER_CODE_DUMPINFO:
        {
            m_listLock.Lock();

            ForEachDLinkNode(DbgStubSink *, pDbgStubSink, &m_list) {
                kprintf("DbgServiceName = %S, Debugger Addr = %08x \n",
                        pDbgStubSink->m_wszServiceName,
                        pDbgStubSink->m_pDebugger);
            }

            m_listLock.Unlock();

        }
        break;

        default:
            ec = E_INVALID_ARGUMENT;
    }

    return ec;
}

void DbgStubManager::Dispose()
{
    return;
}

static DbgStubManager s_dbgStubManager;

EXTERN IDeviceDriver * CDECL CreateDbgStubManager(
    uint_t uDeviceNo, void *pvParameter)
{
    return &s_dbgStubManager;
}
